// TClipboard.java
// This is the main program for this example.

package com.ibm.tspaces.examples.copypaste;

import java.util.*;
import java.io.*;
import java.awt.*; 
import java.awt.event.*;
import java.awt.datatransfer.*;
import com.ibm.tspaces.*;

/*
**  Licensed Materials - Property of IBM
**
**  (C) COPYRIGHT IBM Corp. 1996, 1997  All rights reserved
**
**  US Government Users Restricted Rights - Use, duplication or
**  disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**/
/**
** This is a sample application to show the usefulness of TupleSpace.
** It is a cut and paste buffer that works across programs, systems,
** platforms, and the universe. 
**
** This version is a rewrite of the simple prototype written by Matthias.
** The simple version used just the java cut and paste buffer that could
** hold only a single entry in the buffer -- not all that useful.  There was
** no way to query the contents of the clipboard database to choose amoung
** multiple entries -- only one entry was permitted.
**
** Part of the problem is that the Java cut and paste buffer itself is
** somewhat brain-damaged.  It is a limitation of the buffer that it can
** handle only a single entry.  Thus, we must add our own selection window
** to talk to tuplespace and query its contents.  Then, when we find what
** we want in the tuplespace, then we can place that in the cut and paste
** buffer for use by the local system.
**
** The global cut-n-paste buffer concept is appealing and potentially very
** useful, but if done wrong it can be painful to use.  As a result, we'll
** probably have to experiment with several things before we get it right.
**
** So, the starting idea is this.  We have a Tuplespace communication frame
** that shows what's in the tuplespace.  We should be able to fill up the
** communication frame with entries from the tuplespace.  Users can interact
** with the frame to pick the TS entry that they want, then they can have
** that entry be pasted into the local system cut and paste buffer.  From
** there, the entry can be dropped into any application.
** 
** Version 1.1
** We're going to automatically number the individual entries in the
** communication frame.  The user will use the mouse to select one of the
** numbers to be used in the query back to TS to get the actual entry,
** which will then be placed in the cut and paste buffer.
**
** Version 1.2 (The first GCSClipboard2)
** THIS IS FOR DEMO PURPOSES ONLY!  Don't distribute it too widely until
** we know that the ReTaker class is not causing serious damage to anybody.
**
** The UI has changed quite a bit:  The top TextArea displays the current
** contents of the local clipboard.  The other TextAreas display the contents
** of the user's buffers in TupleSpace.  The user can select the active 
** buffer, and either Read the buffer into the local clipboard or Publish 
** the local clipboard into the buffer. (swm)
**
** Version 1.2.1 has had a couple of enhancements. (jthomas)
**
**    It is now called TClipboard to match the BlueSpaces component
**
**    The current retake method causes a problem if you you try to start
**    2 instances on the same system because they fight over ownership of
**    the system clipboard.  This hacks it a bit more to recogonise this 
**    and one of the instance will give up.  
**   
**    It now uses eventRegister so it will be informed when other TClipboard 
**    instances update on of the buffers.   
**
**    It now updates the local clipboard display consistantly
**
**    It now has Copy/Paste buttons instead of read/Publish because these are
**    more familiar clipboard actions.  
** 
**    AutoCopy and AutoPaste options added to automate the copy/paste between systems
**
** Version 1.3 should probably implement the multi-user version, so you
** can look at other user's buffers (but probably not publish into them).
** 
** A rudimentary multi-user version has been implemented. 
** It allows one user to look at (view) another user's buffers, 
** but does not allow him to modify them. 
**
** In the earlier version, a bug was detected. If another 
** user wrote a tuple into the same tuplespace as the present
** user, it appeared on the present user's clipboard. This has 
** been fixed.
** 
** Version 1.4 should probably implement non-text transfers.
**
** @author Matthias Eichstaedt, June 97
** @author Toby Lehman, Sept 97
** @author Steve McLaughry, Sept 97
** @author John Thomas, Dec 97
** @author Thomas Baby, Jun 98
** @version $Revision: 2.1 $ $Date: 1999/11/05 22:22:38 $
*/

  
public class TClipboard extends Frame 
implements ClipboardOwner, Callback {

  /*
  ***************************************************************************
  ** Members **
  *************
  **
  */


  //
  // The number of buffers available.
  //
  public static int _NUMBER_OF_BUFFERS = 3;

  //
  // The default name of the tuple space to use for the clipboard.
  //
  public static final String  _DEFAULTSPACENAME = "clipboard";

  //
  // The default name of the server hosting the tuple space for the clipboard.
  //
  public static final String  _DEFAULTSERVERNAME  = "localhost";
  
  //
  // The Default name of the user
  //
  public static final String    _DEFAULTUSERNAME = "localuser";  

  //
  // The constants that label the buttons
  //
  public static final String  _PUBLISH_BUTTON = "Publish";
  public static final String  _READ_BUTTON = "Read";
  

  public static final String  _VIEW_BUTTON = "View";
  public static final String  _EXIT_BUTTON = "Exit";
  public static final String  _OK_BUTTON = "OK";
	public static final String  _VIEW_DIALOG_TITLE = "View another clipboard";

  //
  // The name of the TupleSpace to be used for the clipboard
  //
  private String    _spaceName  = "";

  //
  // The name of the server that hosts the tuple space  
  //
  private String    _serverName = "";
  
  //
  // The USER that is running this instance
  //
  private        String         _userName        = "";

  //
  // The tuple space  
  //
  private TupleSpace  _ts   = null;

  // 
  // The current buffer
  // 
  private int _currentBuffer = 1;

  TextArea    _localClpBrdContents;
  Vector      _bufferContents;
  Button      _readButton;
  Button      _publishButton;
  
  Button      _viewButton;
  Button      _exitButton;
  Choice      _bufferChoice;

  Vector      _viewers;

  Clipboard _clipboard = getToolkit().getSystemClipboard();
  
  StringSelection _contents;
  
  /* 
  ** Use the following in lostOwnership to make sure we are not in an
  ** ownership battle.
  */
  int			_lostCount=0;
  long			_lostTimer=0;
  boolean		_lostInactive = false;
  
  /*
  ** Use the following to support AutoCopy and AutoPaste
  */
  public static final String  _AUTO_PASTE_LABEL = "AutoPublish";
  public static final String  _AUTO_COPY_LABEL = "AutoRead";
  
  Checkbox	  _autoCopyBox;
  boolean	  _autoCopyFlag = false;
  Checkbox    _autoPasteBox;
  boolean     _autoPasteFlag = false;
	  
  /*
  ***************************************************************************
  ** TClipboard **
  ******************
  */
  /**
  ** Layout the frame and the buttons for the TClipboard Frame. 
  ** Then, attach
  ** to the tuplespace (create an instance of the client tuplespace object).
  **
  ** @param serverName_ Specify which server we're going to talk to
  ** @param userName_ Specify which user we're acting for
  ***************************************************************************
  */
  public TClipboard(String serverName_, String userName_) {
    super("TSpaces Clipboard");
		
    if( serverName_ == null ) {
      _serverName = _DEFAULTSERVERNAME;
    } 
    else {
      _serverName = serverName_;
    }
    
    if( userName_ == null ){
      _userName = _DEFAULTUSERNAME;
    } 
    else {
      _userName = userName_;
    }
     // Get the tuple space
    _spaceName = _DEFAULTSPACENAME;
    
    Debug.out("Server="+_serverName+
        "; Username="+_userName+
        "; Spacename="+_spaceName);
    
    addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				dispose();
			  TClipboard.this.closeTS();
				System.exit(0);
			}
		});
    _viewers = new Vector();  // no viewers right now

    GridBagLayout gridbag = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();
    setLayout(gridbag);

    Label lab = null;

    // Layout the local clpbrd contents text box
    c.gridwidth = GridBagConstraints.REMAINDER;

    lab = new Label(" Local Clipboard Contents : ");
    gridbag.setConstraints(lab, c);
    add(lab);
    
    _localClpBrdContents = new TextArea(3, 60);
    gridbag.setConstraints(_localClpBrdContents, c);
    add( _localClpBrdContents );


    // Layout the buffer-contents textAreas
    _bufferContents = new Vector(_NUMBER_OF_BUFFERS);
    for (int i=1;i<=_NUMBER_OF_BUFFERS;i++) {
      lab = new Label(" Buffer " + i + " Contents : ");
      gridbag.setConstraints(lab, c);
      add(lab);

      TextArea tempArea = new TextArea(3, 60);
      // We don't let user modify this currently.
      tempArea.setEditable(false);
      gridbag.setConstraints(tempArea, c);
      add( tempArea );

      _bufferContents.addElement( tempArea );
    }
    
    // Layout the buttons
    c = new GridBagConstraints();
    c.gridx = 0;

    // READ Button  
    // Button will Read from TupleSpace and Copy to SystemClipboard
    _readButton = new Button( _READ_BUTTON );
    _readButton.addActionListener(  new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        TClipboard.this.actionRead();
      }
    } );
    gridbag.setConstraints(_readButton, c);
    add(_readButton);

    c = new GridBagConstraints();

    // PUBLISH Button
    // Button will "paste" from SystemClipboard into TupleSpace buffer

    _publishButton = new Button(_PUBLISH_BUTTON);
    _publishButton.addActionListener(  new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        TClipboard.this.actionPublish();
      }
    } );
    gridbag.setConstraints(_publishButton, c);
    add(_publishButton);

    // VIEW Button
    _viewButton = new Button(_VIEW_BUTTON);
    _viewButton.addActionListener(  new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        TClipboard.this.actionView();
      }
    } );
  	gridbag.setConstraints(_viewButton, c);
    add(_viewButton);

    // EXIT Button
    _exitButton = new Button(_EXIT_BUTTON);
    _exitButton.addActionListener( new ActionListener() {
      public void actionPerformed( ActionEvent e ) {
        
        Debug.out("Exiting...");
        TClipboard.this.closeTS();
        TClipboard.this.dispose();
        System.exit(0); // Get outta here!
      }
    } );
    gridbag.setConstraints(_exitButton, c);
    add(_exitButton);
    

    // Label for buffer selection Choice object
    lab = new Label("  Active Buffer : ");
    gridbag.setConstraints(lab, c);
    add(lab);
    
    // Buffer selector (Choice object)
    _bufferChoice = new Choice();
    for (int i=1; i<=_NUMBER_OF_BUFFERS; i++) {
      Integer newInt = new Integer(i);
      _bufferChoice.add(newInt.toString());
    }
    _bufferChoice.addItemListener( new ItemListener() {
      public void itemStateChanged(ItemEvent e) {
        TClipboard.this.actionBufferChange( e );
      } 
    } );
    gridbag.setConstraints(_bufferChoice, c);
    add(_bufferChoice);

    // AutoCopy Button  
    // Button will automate the process by turning on the AutoCopyFlag 
    // When the active Buffer is updated it will automatically
    // copy the info to SystemClipboard
    _autoCopyBox = new Checkbox( _AUTO_COPY_LABEL,_autoCopyFlag );
    _autoCopyBox.addItemListener(  new ItemListener() {
      public void itemStateChanged( ItemEvent e ) {
        _autoCopyFlag = ! _autoCopyFlag;
      }
    } );
    gridbag.setConstraints(_autoCopyBox, c);
    add(_autoCopyBox);

    c = new GridBagConstraints();

    // AutoPaste Button
    // Button will automate the process by turning on the AutoPasteFlag 
    // When the clipboard is updated it will automatically
    // paste the info to the active Buffer
    

    _autoPasteBox = new Checkbox(_AUTO_PASTE_LABEL,_autoPasteFlag);
    _autoPasteBox.addItemListener(  new ItemListener() {
      public void itemStateChanged( ItemEvent e ) {
        _autoPasteFlag = ! _autoPasteFlag;
      }
    } );
    gridbag.setConstraints(_autoPasteBox, c);
    add(_autoPasteBox);


    // One more label, to make things line up nicely
    lab = new Label("");
    gridbag.setConstraints(lab, c);
    add(lab);

    pack();

    this.setupTS();          // Setup the TupleSpace
    
       
  } // TClipboard
  /*
  ***************************************************************************
  ** setupTS **
  *****************/
  /**
  **   Setup the TupleSpace that we will use for the clipboard actions
  **  
  **   
  */

  private void setupTS() {
      
    try {
      	_ts = new TupleSpace( _spaceName, _serverName );
      	// place ourselves on the list to notified when anyone anywhere
	  	  // writes a matching Tuple to this TupleSpace. 
	    TClipboardTuple match = new TClipboardTuple(_userName);   				
			int seqNum = _ts.eventRegister(TupleSpace.WRITE,match, this);
    } 
    catch (TupleSpaceException tse) {
      Debug.out( "1. Caught a TupleSpace Exception: \"" +
                          tse.getMessage() + "\"" );
      tse.printStackTrace( System.out );
    }

    // Get the current contents of the local system clipboard
    takeOwnership();

    // Get the current contents of all the buffers
    Debug.out("About to refresh");
    actionRefresh();
    
  } // setupTS
  /*
  ***************************************************************************
  ** closeTS **
  *****************/
  /**
  **   Close down the TS stuff
  **  
  **   
  */

  private void closeTS() {
      
    
    TupleSpace.cleanup();
      
    
  }   // closeTS
  
  
  
  
  /*
  ***************************************************************************
  ** actionRead **
  ****************
  */
  /**
  ** Read an object from the TS and Copy it into the local buffer.
  ***************************************************************************
  */
  protected void actionRead() {
    TClipboardTuple contentTuple = null;
    String srcData = "";

    Debug.out("About to move data from TS Buffer " + _currentBuffer + " to Local");
    try {
      contentTuple = 
        (TClipboardTuple) _ts.read( new TClipboardTuple( _userName, 
                                                         _currentBuffer ) );
                                                               
      if (contentTuple != null)
        srcData = contentTuple.getContent();
      Debug.out("data = " + srcData); 
      
    } // try
    catch(TupleSpaceException tse) {
      Debug.out( "2. Caught a TupleSpace Exception: \"" +
                          tse.getMessage() + "\"" );
      tse.printStackTrace( System.out );
    } // catch
    
    if (srcData != null) {
    	Debug.out("Copying to System Clipboard: " + srcData );	
      _contents = new StringSelection(srcData);
      _clipboard.setContents(_contents, this);
       // Post the current clipboard contents to the window:
      _localClpBrdContents.setText(srcData);
		} 
		else 
			Debug.out("Nothing to copy");
  } // actionRead

  /*
  ***************************************************************************
  ** actionPublish **
  ************************
  */
  /**
  ** Paste an object from the local buffer and put it into TS.
  **  
  ***************************************************************************
  */
  protected void actionPublish()
  {
    Debug.out("About to move From local to TS");
    Transferable content = _clipboard.getContents(this);
    if (content != null) {
      try {
        String dstData = (String)content.getTransferData(DataFlavor.stringFlavor);
        Debug.out("Data to move : " + dstData );              

        // Remove the current contents of the viewing screen so that
        // we can see the current text that we're putting down.
        ((TextArea)_bufferContents.elementAt(_currentBuffer-1)).setText(dstData);

        // Get rid of the old contents in the TupleSpace.
        _ts.take(new TClipboardTuple( _userName, _currentBuffer )); 

        // Put in the new contents.
        _ts.write(new TClipboardTuple( _userName, _currentBuffer, dstData));
      } 
      catch (Exception e) {
        Debug.out("Couldn't get contents in format : " +
                           DataFlavor.stringFlavor.getHumanPresentableName()); 
      }
    }
  } // actionPublish

  /*
  ***************************************************************************
  ** actionRefresh **
  *******************
  */
  /**
  ** Get the contents of each buffer, display it in the corresponding
  ** TextArea.
  ***************************************************************************
  */
  protected void actionRefresh()
  {
    for (int i=1; i<=_NUMBER_OF_BUFFERS; i++) {
      TClipboardTuple resultTuple = null;
      String cbData = "";
      try {
        resultTuple = (TClipboardTuple)_ts.read( new TClipboardTuple( _userName, i ) );
        if (resultTuple != null)
          cbData = resultTuple.getContent();
      } 
      catch( TupleSpaceException tse ) {
        Debug.out( "3. Caught a TupleSpace Exception: \"" +
                            tse.getMessage() + "\"" );
        tse.printStackTrace( System.out );
      } // catch

      Debug.out("refresh read tuple : " + resultTuple );
    
      ((TextArea)_bufferContents.elementAt( i-1 )).setText(cbData);
    } // for
  } // actionRefresh

  /*
  ***************************************************************************
  ** actionView **
  ****************
  */
  /**
  ** Open a GCSClipboardReader looking at another user's buffers.
  ***************************************************************************
  */
  protected void actionView()
  {
  	TClipboardTuple contentTuple = null;
  	String srcData = "";
		TupleSpace viewts = null;
		
    Debug.out("actionView called");

    ViewChooserDialog vCD = new ViewChooserDialog(this);
    Debug.out("window shown");
    vCD.show();

    String userName = vCD._userName.getText();
    String server = vCD._server.getText();

    if ( userName.equals("") )
      userName = TClipboard._DEFAULTUSERNAME;
    if ( server.equals("") )
      server = TClipboard._DEFAULTSERVERNAME;

    
    Debug.out("user : " + userName +";  server : " + server );
    
     _spaceName = _DEFAULTSPACENAME;
     try {
		 		viewts = new TupleSpace(_spaceName, server);
		 		//viewts.take(new TClipboardTuple( "kevin", )); 
		 		//viewts.take(new TClipboardTuple("kevin", 1, "Hello this is thomas"));
		 		//viewts.take(new TClipboardTuple("kevin", 2, "How are you today"));
		 		//viewts.take(new TClipboardTuple("kevin", 3, "Take it easy..."));
		 } 
		 catch(TupleSpaceException tse) {
		 		Debug.out("4. Caught a TupleSpace Exception: \"" +
		 												tse.getMessage() + "\"");
				tse.printStackTrace(System.out);
		 }
	
  	 ClipboardViewer newViewer = new ClipboardViewer( server, userName );
		 newViewer.show();
	   TClipboard.this._viewers.addElement(newViewer); //might want to talk to it later
		 
		 for(int i = 1; i <= _NUMBER_OF_BUFFERS; i++) {		 		
		 		Debug.out("About to view data in TS Buffer " + 
		 														i + "of user " + userName );
				try {
				  srcData = null;
					contentTuple = 
					 	(TClipboardTuple)viewts.read(new TClipboardTuple(userName, i));
					 	if(contentTuple != null)
					 		srcData = contentTuple.getContent();
					  Debug.out("data = " + srcData);
				} //try
				catch(TupleSpaceException tse) {
					Debug.out(tse);
					tse.printStackTrace(System.out);
				} //catch
        
				if(srcData != null) 					
					((TextArea)newViewer._bufferContents.elementAt( i-1 )).setText(srcData);
			
         
			} //for	
      newViewer.show();
  }//actionView

  /*
  ***************************************************************************
  ** actionBufferChange **
  ************************
  */
  /**
  ** Change the currently active buffer
  ***************************************************************************
  */
  protected void actionBufferChange( ItemEvent e ) {
    Debug.out("actionBufferChange called : " + e.getItem());
    String s = (String)e.getItem();

    for (int i=1; i<=_NUMBER_OF_BUFFERS; i++) {
      Integer newInt = new Integer(i);
      if (s.equals(newInt.toString())) {
        _currentBuffer = i;
        break; // get out of for loop
      } // if
    } // for
  } // actionBufferChange
 
  

     
   

  /*
  ***************************************************************************
  ** takeOwnership **
  *******************
  */
  /**
  ** Called during initialization to get the current clipboard contents 
  ** for display and then take Ownership of the system clipboard
  **
  ***************************************************************************
  */
  public void takeOwnership() 
  {
      // Get the current clipboard contents into currText and specify that
      // this object is to be the new ClipbooardOwner 
      Transferable content = _clipboard.getContents(this);
      String currText = "";
      if (content != null) {
        try {          
          Debug.out("Content="+content.getClass().getName());
          if (content.isDataFlavorSupported (DataFlavor.stringFlavor)) {
            currText = (String)content.getTransferData(DataFlavor.stringFlavor);
            Debug.out("StringFlavor from clipboard : " + currText ); 

          } else if (content.isDataFlavorSupported (DataFlavor.plainTextFlavor)) {
            StringReader sr = (StringReader)content.getTransferData(DataFlavor.plainTextFlavor);
            StringBuffer sb = new StringBuffer();
            
            while(true) {
              int c = sr.read();
              if ( c == -1) 
                break;
              sb.append((char)c);
            }
            currText = sb.toString();
            Debug.out("plainTextFlavor from clipboard : " + currText ); 
          
          } else {
            DataFlavor[] flavors = content.getTransferDataFlavors();
            //Debug.out(flavors);
            Debug.out("Clipboard contents have format: ");
            for(int i=0;i<flavors.length;i++) 
              Debug.out(flavors[i].getHumanPresentableName());  
            currText = "*** NON-TEXT DATA IN CLIPBOARD, CANNOT PUBLISH ***";
          }
        } catch (Exception e) {
          Debug.out(e);
          currText = "";
        }
      } else {
        Debug.out("Clipboard is empty!");
        currText = "";  
        content = new StringSelection("");  // we can't post null back to clipboard
      }        
                   
      
      // Post the current clipboard contents to the window:
      _localClpBrdContents.setText(currText);

      // paste original contents back to clipboard (thus making us the owner )
      _clipboard.setContents(content,this);
    
  } // takeOwnership
  
  
  /*
  ***************************************************************************
  ** lostOwnership **
  *******************
  */
  /**
  ** lostOwnership method is required to implement the ClipboardOwner interface.
  **
  ** Called when another application replaces the string we had in the 
  ** clipboard.  Unfortunately, it seems to be called AFTER our string
  ** (which we're also given as oldContents_) has been taken out, but
  ** BEFORE the new contents are added.  So start another thread to 
  ** re-take ownership, but return from this call.
  **
  ** It is possible that another application may also try to keep 
  ** ownership of the clipboard this way, perhaps even another instance
  ** of TClibboard.  so we try to handle this.
  ** We will set a time limit of 10 seconds and count all of the 
  ** lostOwnership events that occur in that time limit.  If it is 
  ** more than some number (say 10), then we will give up ownership.
  **
  ** @param clipboard_ the clipboard that we lost
  ** @param oldContents_ what we had in the clipboard
  ***************************************************************************
  */
  public void lostOwnership(Clipboard clipboard_, Transferable oldContents_) 
  {
    // First, a little error checking:
    if (clipboard_ != _clipboard) {
      Debug.out("lostOwnership(): ignore because never owned that clipboard");
      return;
    }
    if (_lostInactive )       // Inactive system clipboard 
    	return; 
    long now = (new Date()).getTime();
    if ( _lostTimer == 0 ) {
    	_lostTimer = now + 10*1000;
    	_lostCount = 0;
    }  else {
    	if ( now < _lostTimer) 
    		_lostCount++;
    	else {
    		_lostCount = 0;
    		_lostTimer = 0;
    	}
    }
    
	if (_lostCount > 10 ) {   // greater than 10 in 10 seconds 
		Debug.out("Giving up control of Clipboard");		
    	_localClpBrdContents.setText("Giving up control of Clipboard...Inactive");
		_lostTimer = 0;
		_lostInactive = true;
		return;
	}
		
    Debug.out("lostOwnership: OldContents=" + oldContents_);
      
    ReTaker rt = new ReTaker(clipboard_, this);
    rt.start();
  } // lostOwnership

    /*
    ***************************************************************************
    ** ** call **
    ** **********
    */
    /**
    ** Process the callback from the server that notifies us when anyone 
    ** (including ourselfes) writes to the clipboard TupleSpace.
    **
    ** 
    ** @param eventName_ the name of the event command that caused this call, that is the
    ** name of the client side command of the thread that registered this call, e.g., in the
    ** case of a read, this would be TupleSpace.READ, **not** TupleSpace.WRITE which is the
    ** corresponding command that caused the actaul event.
    ** @param tsName_ the name of the tuple space this command was executed on.
    ** @param sequenceNumber_ the sequenceNumber for this event/command
    ** @param tuple_ the returned tuple or a Tuple with an exception inside
    ** @param isException_ was the command processed normaly or was there an exception
    **
    ** @return true if this is the last call this sequence # should be getting otherwise false
    *************************************************************************** 
    */
    public synchronized boolean 
    call(String eventName_, String tsName_, int sequenceNumber_, SuperTuple tuple_, 
										boolean isException_) {
      try {                
        Debug.out("call()");
        String cbData = "";
        int buffer=0;
        if ( isException_ ) {
          Debug.out("Exception received in callback: "+ tuple_.getField(0).getValue());
          
        } else {
          TClipboardTuple cbt = (TClipboardTuple)tuple_;
            
          buffer = cbt.getBufferNum();          
          cbData = cbt.getContent();
        
      
           ((TextArea)_bufferContents.elementAt( buffer-1 )).setText(cbData);
           if (_autoCopyFlag && (buffer == _currentBuffer)) {
              
             Debug.out("Copying to System Clipboard: " + cbData );  
             _contents = new StringSelection(cbData);
             _clipboard.setContents(_contents, this);
              // Post the current clipboard contents to the window:
             _localClpBrdContents.setText(cbData);
           }
        }
    } catch( TupleSpaceException tse ) {
      Debug.out( tse);
    
      return false;
    } // catch
		return false;
	} //call

  /*
  ***************************************************************************
  ** Inner classes defined here **
  ********************************
  */
  
  /*
	***************************************************************************
	** class ClipboardViewer **
	***************************
	*/
  
 class ClipboardViewer extends Frame{
		public Vector _bufferContents;		
		
		public ClipboardViewer(String server_, String user_) {			
			super("ClipBoard Viewer");
      addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        dispose();        
      }
    });
     GridBagLayout gbag = new GridBagLayout();
			setLayout(gbag);
			
			GridBagConstraints c;			
			c = new GridBagConstraints();	
			c.gridwidth = GridBagConstraints.REMAINDER;
			
			_bufferContents = new Vector(_NUMBER_OF_BUFFERS);
			for(int i = 1; i <= _NUMBER_OF_BUFFERS; i++) {
				TextArea buffer = new TextArea(3, 60);
				gbag.setConstraints(buffer, c);
				add(buffer);
				_bufferContents.addElement(buffer);
			} //for
			pack();      
		} //Constructor 
	} //ClipboardViewer
		
  /*
  ***************************************************************************
  ** class ReTaker **
  *******************
  */
  class ReTaker extends Thread {
    private Clipboard _clipboard = null;
    private ClipboardOwner _owner = null;
    public ReTaker(Clipboard clipboard_, ClipboardOwner owner_) {
      _clipboard = clipboard_;
      _owner = owner_;
    }
    
    /*
    ***************************************************************************
    ** ReTaker::run **
    ******************
    /**
    ** "It's not a hack, it's a technique."
    **
    ** Called by the lostOwnership method above.
    **
    ** We're gonna be sneaky here: First, we wait 0.25 second for the system
    ** to post the new contents to the clipboard.  Then we take what's just been
    ** posted to the clipboard, grab a copy (if it's text), and re-post it, which
    ** makes us the owner of the clipboard again.  This is necessary, because if
    ** we allow ownership to be taken away, we won't get notified next time the 
    ** contents change.  Let's just hope nobody else was counting on being
    ** the owner of the clipboard for more then 0.25 seconds.
    ***************************************************************************
    */
    public void run() {
      String currText = ""; 

      Debug.out("re-taker sleeping");
      
      try {
        sleep(250); // sleep for 0.25 second, waiting for paste to complete
      } catch ( InterruptedException ie ) {
        Debug.out("Somebody woke up the re-taker!");
      }

      Debug.out("re-taker running");

      // Get the new clipboard contents into currText
      Transferable content = _clipboard.getContents(this);
      
      if (content != null) {
        try {
          currText = (String)content.getTransferData(DataFlavor.stringFlavor);
        } catch (Exception e) {
          Debug.out(e);
          Debug.out("Lost ownership but couldn't get contents in format: "+
                               DataFlavor.stringFlavor.getHumanPresentableName());  
          currText = "*** NON-TEXT DATA IN CLIPBOARD, CANNOT PUBLISH ***";
        }
      } else {
        Debug.out("But clipboard is empty!");
      }
        
      //Debug.out("Data from clipboard : " + currText );              
      
     
      // Post the current clipboard contents to the window:
      _localClpBrdContents.setText(currText);
	  if (_autoPasteFlag) 
	  	TClipboard.this.actionPublish();
      // paste currText back to clipboard (thus making us the owner again)
      _clipboard.setContents(content,_owner);
    }
  } // class ReTaker

  /*
  ***************************************************************************
  ** class ViewChooserDialog **
  *****************************
  */
  class ViewChooserDialog extends Dialog {
    public TextField _userName = new TextField( 20 ); // 20 characters long;
    public TextField _server = new TextField( 20 ); // 20 characters long;

    public ViewChooserDialog(Frame parent_)
    {
      super(parent_, TClipboard._VIEW_DIALOG_TITLE, true);

      GridBagLayout gridbag = new GridBagLayout();
      setLayout(gridbag);

      GridBagConstraints c = new GridBagConstraints();
      c.gridwidth = GridBagConstraints.REMAINDER;
      c.gridx = 0;
      c.gridy = 0;

      // User name label
      Label lab = new Label("User name: ");
      gridbag.setConstraints(lab, c);
      add(lab);

      c.gridy = 1;			
      // User name TextField
      gridbag.setConstraints(_userName, c);
      add(_userName);

      c.gridy = 2;
      // Server name label
      lab = new Label("Server name: ");
      gridbag.setConstraints(lab, c);
      add(lab);

      c.gridy = 3;
      // Server name TextField
      gridbag.setConstraints(_server, c);
      add(_server);

      c.gridy = 4;
      // OK button
      Button okButton = new Button(TClipboard._OK_BUTTON);
      okButton.addActionListener( new ActionListener() {
	  public void actionPerformed( ActionEvent e ) {
	    Debug.out("OK button clicked.");
	    ViewChooserDialog.this.dispose();
	  }
      } );
      gridbag.setConstraints(okButton, c);
      add(okButton);

      pack();
    }
  } // class ViewChooserDialog 


  /*
  ***************************************************************************
  ** main **
  **********
  */
  /**
  ** The main man.  We process the command line arguments, then creates a clipboard
  ** object.
  ** <pre>
  **   Usage:   classname options  userName 
  **        options: 
  **          -D            - turn on Debug flag
  **          -h host       - Specify TS Server name
  **          -userName     - Username field for clipboard Tuples,
  ** </pre>
  ** @param args_ Command line arguments
  ***************************************************************************
  */
  public static void main(String[] argv) {
    String  tsServerName  = _DEFAULTSERVERNAME;
    String  tsUserName = _DEFAULTUSERNAME;
    
    // standard processing of operands
    if ( argv.length == 0) 
      System.out.println("Usage:  TClipboard [-D] [-h tsHost] username");
    for (int i=0; i< argv.length; i++) {
      if (argv[i].equals("-D") )
        Debug.setDebugOn(true);
      else if (argv[i].equals("-h")) {
        tsServerName = argv[++i];        
      }
      else 
        tsUserName = argv[i];
    }
		
    
    TClipboard gcsc = new TClipboard( tsServerName, tsUserName );
    Debug.out("Go to show window ");
    gcsc.show();
    
  } // main
} // TClipboard

/*
$History: TClipboard.java $
 * 
 * *****************  Version 7  *****************
 * User: Jthomas      Date: 10/20/98   Time: 11:41a
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/copypaste
 * add some debug code for problems with dataflavor
 * 
 * *****************  Version 6  *****************
 * User: Jthomas      Date: 10/14/98   Time: 9:40a
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/copypaste
 * General cleanup and a few needed fixes
 * 
 * *****************  Version 5  *****************
 * User: Thomas       Date: 8/28/98    Time: 7:21p
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/copypaste
 * uh
 * 
 * *****************  Version 4  *****************
 * User: Toby         Date: 3/23/98    Time: 8:45a
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/copypaste
 * 
 * *****************  Version 3  *****************
 * User: Toby         Date: 3/23/98    Time: 8:44a
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/copypaste
 * Had to export a few things (make them public) so that other classes
 * could share in the joy of copypaste
 * 
 * *****************  Version 2  *****************
 * User: Jthomas      Date: 1/27/98    Time: 11:15p
 * Updated in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/copypaste
 * changed BlueClipboard to TClipboard
 * 
 * *****************  Version 1  *****************
 * User: Jthomas      Date: 1/27/98    Time: 5:50p
 * Created in $/GCS/Development/TSpaces/Java/com/ibm/tspaces/examples/copypaste
 * 
 */
/* $Log: TClipboard.java,v $
/* Revision 2.1  1999/11/05 22:22:38  estesp
/* Update revision number to 2.1 on all files
/*
/* Revision 1.1.1.1  1999/11/05 16:24:53  estesp
/* Imported 2.1.0 release into Austin CVS
/*
 * Revision 1.3  1999/09/13 05:09:48  jthomas
 * fix to handle exception callback
 *
 * Revision 1.2  1999/06/17 05:39:37  thodes
 *   Updated the inital checkin to append the "Log" field and
 * convert CR/LF to CR (for win/unix interop).
 *
 *   The inital checkin lower-cased directrory names, and omitted
 * many dirs/files that will added later, e.g., MoDAL stuff.
 *
 * All of the above will need to be done to all VSS-to-CVS
 * converted sources.  In summary, in moving from VSS to CVS, do this:
 *  - lower-case dir names
 *  - CR/LF --> CR
 *  - append CVS "Log" entties
 *
 * (a script that helps with #2 and #3 is in
 *     tspaces1:~thodes/bin/update-keywords.sh)
 * 
 */

